#include "calls.h"
#include "../../semantics/method.h"
#include "../../semantics/field.h"
#include "../../semantics/error.h"
#include "../../semantics/visitordata.h"
#include "../../semantics/classsemantic.h"
#include "../../code/callfactory.h"
#include "../../code/opcodes.h"
#include "classnode.h"

AttributeCommand::~AttributeCommand()
{
}

AttributeAccess::~AttributeAccess()
{
	if (this->isOwningData())
	{
		delete m_field;
		if (m_kill)
		{
		 delete m_exp;
		 delete m_args;
		}
		delete m_behaviour;
	}
}
void AttributeAccess::write(GraphStream * s)
{
	this->accept(s);
	fprintf(s->stream,"\tnode_%d [label=\"(%d,%d)\\nAttributeAccess node (%s)\" shape=\"rectangle\"]\n", this->number(),this->line(),this->position(),this->m_field->c_str());
	if (m_exp)
	{
		m_exp->write(s);
		fprintf(s->stream,"\tnode_%d -> node_%d [label=\"expression\"]\n",this->number(),m_exp->number());
	}
	if (m_args)
	{
	 m_args->write(s);
	 fprintf(s->stream,"\tnode_%d -> node_%d [label=\"arguments\"]\n",this->number(),m_args->number());
	}
}


bool AttributeCommand::canBeTrasnformedFromAssignment(ParentData * parent, bool * ok)
{
	return false; 
}
bool FieldGet::canBeTrasnformedFromAssignment(ParentData * parent, bool * ok)
{
	return true;
}
bool AttributeAccess::selectFieldOrMethod(ParentData * data, bool field)
{
	if (m_visited)
		return m_behaviour!=NULL;
	m_visited=true;
	std::string className=m_exp->getType(data);
	m_class_data=data->Classes->getClassByName(className);
	if (m_class_data)
	{
		if (m_args==NULL)
		{
			 Field * res_field=m_class_data->getFieldByName(*m_field);
			 Method * res_method=m_class_data->getMethodByName(*m_field);
			if (res_field && res_method)
			{
				if (field)
					m_behaviour=new FieldGet(res_field);
				else
					m_behaviour=new MethodCall(res_method);
			}
			if (res_field && m_behaviour==NULL)
				m_behaviour=new FieldGet(res_field);
			if (res_method && m_behaviour==NULL)
				m_behaviour=new MethodCall(res_method);
		}
		if (m_behaviour==NULL)
		{
			Method * tmpmethod=m_class_data->getMethodByName(*m_field);
			if (tmpmethod)
			{
				m_behaviour=new MethodCall(tmpmethod);
				return true;
			}
			else
			{
				data->Classes->add(new FieldOrMethodDoesNotExists(this,*m_field,className));
				return false;
			}
		}
		else
		{
			return true;
		}
	}
	else
		data->Classes->add(new TypeDoesNotExists(m_exp,className));
	return false;
}

bool AttributeAccess::canBeTransformedFromAssignment(ParentData * parent, bool * ok)
{
	bool canSelect=selectFieldOrMethod(parent,true);
	*ok=*ok && !canSelect;
    if (canSelect)
	{
		return m_behaviour->canBeTrasnformedFromAssignment(parent,ok);
	}
	return false;
}

CreationCall::~CreationCall()
{
	if (this->isOwningData())
	{
		delete m_type;
		delete m_identifier;
		delete m_constructor_name;
		delete m_args;
	}
	delete m_info;
}

void CreationCall::write(GraphStream * s)
{
  this->accept(s);
  fprintf(s->stream,"\tnode_%d [label=\"(%d,%d)\\nCreation call node %s.%s\" shape=\"rectangle\"]\n", this->number(),this->line(),this->position(),this->m_identifier->c_str(),(m_constructor_name)?m_constructor_name->c_str():"\\<init\\>");
  
  if (m_type)
  {
    m_type->write(s);
    fprintf(s->stream,"\tnode_%d -> node_%d [label=\"type\"]\n",this->number(),m_type->number());
  }
  if (m_args)
  {
	m_args->write(s);
	fprintf(s->stream,"\tnode_%d -> node_%d [label=\"arguments\"]\n",this->number(),m_args->number());
  }	
  
}

FieldAccess::~FieldAccess()
{
	delete m_args;
}

FieldAccess::FieldAccess(
						 Field * field, 
						 ClassSemantic * semantic,
						 const std::string & className, 
						 const std::string & fieldName,
						 Compound * args):AbstractNode(YYLTYPE()),m_field(field),m_classname(className),m_fieldname(fieldName),m_args(args),m_semantic(semantic)
{
 own();
}

FieldAccess * AttributeAccess::createFieldAccess()
{
  Field * fld=static_cast<FieldGet*>(m_behaviour)->field();
  FieldAccess * fldacc=new FieldAccess(fld,m_class_data,m_class_data->cnode()->name(),*m_field,m_args);
  fldacc->line()=this->line();
  fldacc->position()=this->position();
  fldacc->filename()=this->filename();
  m_kill=false;
  return  fldacc;
}

void FieldAccess::write(GraphStream * stream)
{
}

bool AttributeCommand::checkLogic(ParentData * data)
{
	return false;
}

bool MethodCall::checkLogic(ParentData * data)
{
	return true;
}

bool AttributeAccess::checkLogic(ParentData * data)
{
	selectFieldOrMethod(data);
	if (m_behaviour)
	{
		bool result=m_behaviour->checkLogic(data);
		if (!result)
			data->Classes->add(new ExpressionMakesNoSense(this));
		return result;
	}
	else
		return true;
}

bool FieldGet::isLValue(ParentData * data)
{
	return false;
}

bool MethodCall::isLValue(ParentData *data)
{
	return false;
}

bool AttributeAccess::isLValue(ParentData * data)
{
	selectFieldOrMethod(data);
	if (m_behaviour)
	{
		return m_behaviour->isLValue(data);
	}
	return false;
}

bool CreationCall::checkLogic(ParentData * data)
{
	return true;
}

bool   CreationCall::isResultReturn()
{
	return *m_identifier=="RESULT";
}

void AttributeAccess::checkCommonErrors(ParentData * data)
{
	data->setCurrentAccess(this);
	selectFieldOrMethod(data,false);
	if (m_behaviour)
		m_behaviour->checkCommonErrors(data);
}

std::string AttributeAccess::getType(ParentData *data)
{
	selectFieldOrMethod(data,false);
	this->checkCommonErrors(data);
	if (m_behaviour)
	{
		return m_behaviour->getType(data);
	}
	else return UNKNOWN_TYPE;
}


void FieldGet::checkCommonErrors(ParentData * data)
{
	CurrentAccessPtr ptr;
	data->getAcc(ptr);
	if (m_field->isAvailable(data->CurrentClass->cnode()->name(),data->Classes)==false)
	{
		data->Classes->add(new FieldIsInaccessible(&(*ptr),m_field->m_realname,data->CurrentClass->cnode()->name()));
	}
}

std::string FieldGet::getType(ParentData * data)
{
	return m_field->m_type->fullName();
}


void MethodCall::checkCommonErrors(ParentData * data)
{
	CurrentAccessPtr ptr;
	data->getAcc(ptr);
	if (m_method->isAvailable(data->CurrentClass->cnode()->name(),data->Classes)==false)
	{
		data->Classes->add(new MethodIsInaccessible(&(*ptr),m_method->m_realname,data->CurrentClass->cnode()->name()));
	}
	
	m_info=createCallInformation(ptr->expression(),
								 ptr->callClassData(),
								 m_method,
								 ptr->args(),
								 data);
	
	if (m_method->returnType())
		m_pop_result=true;
}
std::string MethodCall::getType(ParentData * data)
{
	if (m_method->returnType())
	{
		m_pop_result=false;
		return m_method->returnType()->fullName();
	}
	else return "VOID";
}

MethodCall::~MethodCall()
{
	delete m_info;
}


void FieldAccess::checkCommonErrors(ParentData * data)
{
  CurrentAccessPtr ptr;
  data->getAcc(ptr);
  if (m_field->isAvailable(data->CurrentClass->cnode()->name(),data->Classes)==false)
  {
		data->Classes->add(new FieldIsInaccessible(&(*ptr),m_field->m_realname,data->CurrentClass->cnode()->name()));
  }
}

std::string FieldAccess::getType(ParentData * data)
{
	checkCommonErrors(data);
	return m_field->m_type->fullName();
}


void CreationCall::checkCommonErrors(ParentData * data)
{
	ClassTypeNode * idtype=data->CurrentMethod->getArgByName(*m_identifier);
	if (idtype==NULL) 
	{
		data->Classes->add(new LocalVariableNotFound(this,*m_identifier));	
		return;
	}
	m_local_arg_index=data->CurrentMethod->getLocalNumberByName(*m_identifier);
	m_class_data=data->Classes->getClassByName(idtype);
	if (m_type!=NULL)
	{
		ClassSemantic * polymorph=data->Classes->getClassByName(static_cast<ClassTypeNode*>(m_type));
		if (polymorph==NULL)
		{
			data->Classes->add(new ClassDoesNotExistsError(this,static_cast<ClassTypeNode*>(m_type)->fullName()));
		}
		else
		{
			if (data->Classes->isAncestor(idtype->fullName(),static_cast<ClassTypeNode*>(m_type)->fullName())==false)
			{
				data->Classes->add(new NotAnAncestor(this,idtype->fullName(),static_cast<ClassTypeNode*>(m_type)->fullName()));
			}
			m_class_data=polymorph;
		}
	}

	m_invoked_method=m_class_data->getMethodByName(*m_constructor_name);
	if (m_invoked_method==NULL)
	{
		data->Classes->add(new FieldOrMethodDoesNotExists(this,*m_constructor_name,m_class_data->cnode()->name()));
	}
	else
	{
		Constructor * constr=m_class_data->getConstructorByMethodName(*m_constructor_name);
		if (constr==NULL)
		{
			data->Classes->add(new NotAConstructor(this));
		}
		else
		{
			if (constr->isAvailable(data->CurrentClass->cnode()->name(),data->Classes)==false)
				data->Classes->add(new ConstructorIsInaccessible(this,*m_constructor_name,data->CurrentClass->cnode()->name()));
		}
		if (m_class_data->cnode()->type()==CLM_DEFERRED)
		{
			data->Classes->add( new AbstractClassCreation(this) ) ;
		}
		if (*m_identifier=="PRECURSOR" || *m_identifier=="CURRENT")
		{
			data->Classes->add(new PrecursorCurrentCreation(this) );
		}
		
		m_pop_result=m_invoked_method->returnType()!=NULL;
		
		IdentifierNode * node=new IdentifierNode(YYLTYPE(),m_identifier);
		node->filename()=this->filename();
		node->line()=this->line();
		node->position()=this->position();
		m_info=createCallInformation(node,m_class_data,m_invoked_method,m_args,data);
	
		delete node;
	}
	
}

void CreationCall::fillConstant(ParentData * data)
{
	ConstantTable * table=data->currentTable();
	m_class=table->createClass(m_class_data->cnode()->name());
	m_constructor=table->createConstructorRef(m_class_data->cnode()->name());
	m_info->fillConstant(data);
}

void CreationCall::produceCode(ParentData * data)
{
	JBFile * file=data->currentFile();
	file->write(JVMNEW);
	file->write(m_class);
	
	file->write(DUP);
	file->write(ASTORE);
	file->write(m_local_arg_index);
	file->write(DUP);
	file->write(INVOKESPECIAL);
	file->write(m_constructor);
	
	m_info->produceCode(data);
	
	if (m_pop_result)
		file->write(POP);
	
}

void AttributeAccess::fillConstant(ParentData * data)
{
	data->setCurrentAccess(this);
	m_behaviour->fillConstant(data);
}

void AttributeAccess::produceCode(ParentData * data)
{
	data->setCurrentAccess(this);
	m_behaviour->produceCode(data);
}

void FieldGet::fillConstant(ParentData * data)
{
	CurrentAccessPtr ptr;
	data->getAcc(ptr);
	ptr->expression()->fillConstant(data);
	std::string classname,name,descriptor;
	
	classname=m_field->m_classname;
	m_field->getCommonCompilableSignature(name,descriptor);
	m_ref=data->currentTable()->createFieldRef(classname,name,descriptor);
}

void FieldGet::produceCode(ParentData *data)
{
	CurrentAccessPtr ptr;
	data->getAcc(ptr);
	ptr->expression()->produceCode(data);
	JBFile * file=data->currentFile();
	file->write(GETFIELD);
	file->write(m_ref);
}

void MethodCall::fillConstant(ParentData * data)
{
	CurrentAccessPtr ptr;
	data->getAcc(ptr);
	ptr->expression()->fillConstant(data);
	m_info->fillConstant(data);
}

void MethodCall::produceCode(ParentData * data)
{
	CurrentAccessPtr ptr;
	data->getAcc(ptr);
	ptr->expression()->produceCode(data);
	m_info->produceCode(data);
}